home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 April: Mac OS SDK / Dev.CD Apr 00 SDK1.toast / Development Kits / Mac OS / Installer SDK 1.2.3 / Upgrader 1.2.3 & Engines / Upgrader 1.2.3 / Plug-in Examples / SAM Virus Checker Plug-in / Plug-in Sources / SAMLauncher.c next >
Encoding:
C/C++ Source or Header  |  1997-07-24  |  34.1 KB  |  1,137 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        SAMLauncher.c
  3.  
  4.     Contains:    Code for SAM Upgrader Plug-in
  5.     
  6.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  7.  
  8. */
  9.  
  10. #include <Devices.h>
  11. #include <DriverGestalt.h>
  12. #include <SCSI.h>
  13. #include <Start.h>
  14. #include <Aliases.h>
  15. #include <Memory.h>
  16. #include <LowMem.h>
  17. #include <Quickdraw.h>
  18. #include <Files.h>
  19. #include <Resources.h>
  20. #include <TextUtils.h>
  21. #include <TextEdit.h>
  22. #include <MixedMode.h>
  23. #include <Events.h>
  24. #include <Processes.h>
  25. #include <Gestalt.h>
  26. #include <Printing.h>
  27. #include <assert.h>
  28. #include <StandardFile.h>
  29. #include <Types.h>
  30. #include <Folders.h>
  31.  
  32. #include "SAMLauncher.h"
  33.  
  34. // PROTOTYPES
  35.  
  36. static Boolean    HandleEventForPluginModule(EventRecord *inEvent);
  37. static    void     TerminatePluginModule(void);
  38. static ShellErr    HandleMouseDownInSAMPanel(EventRecord *inEvent, Boolean *wasHandled);
  39. static Boolean    HandleMouseDownInHelpPanel(EventRecord *inEvent);
  40. static OSErr    SetupSAMPanel( SInt16 inPrefRsrcID );
  41. static OSErr    SetupHelpPanel( SInt16 inPrefRsrcID );
  42. static void     DisplayHelp(void);
  43. static void     SetRadioButtonState(void);
  44. static void     LaunchSAM( void );
  45. Boolean            IsInstallableTarget ( SInt16 inVRefNum );
  46. VolWhereTypes    WhereVol ( SInt16 vRefNum );
  47. Boolean            IsCompactDisk ( SInt16 pDrRefNum, SInt16 pFSId );
  48. Boolean            IsVolumeInvisible ( SInt16 pVRefNum );
  49. LockTypes        VolLocked ( SInt16 vRefNum );
  50. short             DriveNumToDriverRefNum ( short driveNum );
  51. short            DriverNumberToDriveNumber ( SInt16 pDrRefNum );
  52. short            PStrCmp ( ConstStr255Param p2, ConstStr255Param p1 );
  53. void            VRefNumToVName ( SInt16 vRefNum, Str31 volName );
  54. void            PStrSet ( Str255 destination, ConstStr255Param source );
  55.  
  56. void            AddVolumeToList ( short vRefNum, short count );
  57. unsigned short    CountValidDrives ( void );
  58.  
  59. // GLOBALS
  60. short                gSysVolRefNum = -1;
  61. DialogPtr            gSAMPanel = NULL,
  62.                     gHelpPanel = NULL;
  63. Boolean                gHelpPanelOpen = false;
  64. RGBColor            gBackGroundColor;
  65. RadioButtonStates     gRadioButtonState;
  66. VolumeList            gCurrentVolumeList[kMaxAttachedVolumes];
  67.  
  68. static Boolean QuitProcess(ProcessSerialNumber *inApplicationPSN);
  69. static void SendQuitEventToApplication(void);
  70. static ShellErr SetUpAppleEventHandlers();
  71. static ShellErr RemoveAppleEventHandlers();
  72. pascal OSErr ChildDiedHandler(const AppleEvent *inAppleEvent, const AppleEvent */* outReply */, SInt32 /* inRefcon */ );
  73.  
  74. // GLOBALS
  75.  
  76. Boolean             gApplicationLaunched     = false;
  77. ProcessSerialNumber gApplicationPSN;
  78. AEEventHandlerUPP     gChildDiedHandlerUPP    = NULL;
  79. Boolean             gQuitApponExit             = true;                                    //Set this to true if you want the application to quit when leaving the plugin.
  80. SInt16                gAppRefID                = 0;
  81.  
  82. //--------------------------------------------------------------------------------
  83. //    InitializePluginModule
  84. //--------------------------------------------------------------------------------
  85.  
  86. void InitializePluginModule(void* inPSTable, SInt32 inRefCon, Boolean inEnterAtBeginning)
  87. {
  88. #pragma unused (inEnterAtBeginning)
  89.  
  90.     ShellErr            err;
  91.     short                refNum = -1;
  92.     AuxWinHandle        colorTableHdl;
  93.     SInt32                 info;
  94.     Size                actualSize;
  95.     long                foundDirID = 0;
  96.     
  97.     EnterPlugin();
  98.     SetupPlugin(inPSTable);
  99.     
  100.     err = FindFolder( kOnSystemDisk, kSystemFolderType, kDontCreateFolder, &gSysVolRefNum, &foundDirID );
  101.     
  102.     // Register our event handler routine
  103.     if ( err == noErr )
  104.         err = PSRegisterHandler(kEventHandlerID, (UniversalProcPtr) HandleEventForPluginModule);
  105.     
  106.     if ( err == noErr )
  107.     {
  108.         // Resister our termination handler routine
  109.         err = PSRegisterHandler(kTerminationHandlerID, (UniversalProcPtr) TerminatePluginModule);
  110.  
  111.         if ( err == noErr )
  112.         {
  113.             //Register ChildDied event handler
  114.             err = SetUpAppleEventHandlers();
  115.             
  116.             if ( err == noErr )
  117.                 err = PSSetupNewPanel(kSAMPanelRsrcID, &gSAMPanel);
  118.             
  119.             if ( err == noErr )
  120.             {
  121.                 err = SetupSAMPanel( LoWord( inRefCon) );            
  122.                 if ( err == noErr )
  123.                 {
  124.                     PSGetGlobalData( kVirusPanelButtonState, ( GlobalDataPtr ) &gRadioButtonState, sizeof(RadioButtonStates), &actualSize );
  125.                     
  126.                     SetRadioButtonState();
  127.                     // Set content color to gray.
  128.                     if ( Gestalt( gestaltQuickdrawVersion, &info ) == noErr && info >= gestalt8BitQD && GetAuxWin( gSAMPanel, &colorTableHdl ) ) 
  129.                     {
  130.                         gBackGroundColor = ( *( *colorTableHdl )->awCTable )->ctTable[wContentColor].rgb;
  131.                         ( *( *colorTableHdl )->awCTable )->ctTable[wContentColor].rgb.red        = 0xEEEE;                    // magnitude of red component
  132.                         ( *( *colorTableHdl )->awCTable )->ctTable[wContentColor].rgb.green        = 0xEEEE;                    // magnitude of green component
  133.                         ( *( *colorTableHdl )->awCTable )->ctTable[wContentColor].rgb.blue        = 0xEEEE;                    // magnitude of blue component
  134.                         RGBBackColor( &( *( *colorTableHdl )->awCTable )->ctTable[wContentColor].rgb );
  135.                     }
  136.                     PSShowPanel(gSAMPanel);        
  137.                 }
  138.             }
  139.         }
  140.     }
  141.  
  142.     if (err != noErr)
  143.     {
  144.         PSErrorAlert( err, true, "\p", "\p", "\p", "\p", kQuitButtonIndex, kContinueNotQuitBtnIndex );
  145.         PSQuitShell(kDontAllowUserToContinue);
  146.     }
  147.     ExitPlugin();
  148. }
  149.  
  150. //--------------------------------------------------------------------------------
  151. //    TerminatePluginModule
  152. //--------------------------------------------------------------------------------
  153.  
  154. static void TerminatePluginModule ()
  155. {    
  156.     AuxWinHandle        colorTableHdl;
  157.     SInt32                 info;
  158.  
  159.     EnterPlugin();
  160.     
  161.     PSSetGlobalData( kVirusPanelButtonState, ( GlobalDataPtr ) &gRadioButtonState, sizeof(RadioButtonStates) );        
  162.     // Restore window content color to default.
  163.     if ( Gestalt( gestaltQuickdrawVersion, &info ) == noErr && info >= gestalt8BitQD && GetAuxWin( gSAMPanel, &colorTableHdl ) ) 
  164.     {
  165.         ( *( *colorTableHdl )->awCTable )->ctTable[wContentColor].rgb = gBackGroundColor;
  166.         RGBBackColor( &gBackGroundColor );
  167.     }
  168.     
  169.     if (gQuitApponExit)
  170.     {
  171.         if (gApplicationLaunched)
  172.             SendQuitEventToApplication();
  173.     }
  174.     
  175.     RemoveAppleEventHandlers();
  176.             
  177.     ExitPlugin();
  178. }
  179.  
  180. //--------------------------------------------------------------------------------
  181. //    HandleEventForPluginModule
  182. //--------------------------------------------------------------------------------
  183.  
  184. static Boolean    HandleEventForPluginModule(EventRecord *inEvent)
  185. {
  186.     ShellErr    err;
  187.     Boolean        wasHandled = false;
  188.  
  189.     EnterPlugin();
  190.  
  191.     switch (inEvent->what)
  192.     {
  193.             
  194.         case mouseDown:
  195.             if(FrontWindow() == gSAMPanel)
  196.                 err = HandleMouseDownInSAMPanel(inEvent, &wasHandled);
  197.             else if (FrontWindow() == gHelpPanel)            // check for Help panel
  198.                 wasHandled = PSHandleHelpWindowEvent (gHelpPanel, inEvent);    
  199.             break;
  200.  
  201.         case osEvt:                                                                                        // Resume events only!
  202.             if ( ( ( unsigned long ) inEvent->message >> 24 ) == suspendResumeMessage )
  203.             {
  204.                 if ( ( inEvent->message & resumeFlag ) != 0 )
  205.                     ( void ) PSHandleHelpWindowEvent ( gHelpPanel, inEvent );
  206.             }
  207.             break;
  208.         case kHighLevelEvent:
  209.             AEProcessAppleEvent(inEvent);
  210.  
  211.             wasHandled = true;
  212.             break;
  213.         default:
  214.             break;
  215.         }            
  216.         
  217.  
  218.     ExitPlugin();
  219.     return(wasHandled);
  220. }
  221.  
  222. //--------------------------------------------------------------------------------
  223. //    HandleMouseDownInSAMPanel
  224. //--------------------------------------------------------------------------------
  225. static ShellErr    HandleMouseDownInSAMPanel(EventRecord *inEvent, Boolean *wasHandled)
  226. {
  227.     short        itemHit;
  228.     ShellErr    err = noErr;
  229.     
  230.     *wasHandled = false;
  231.     if (PSGetPanelItemHit(gSAMPanel, inEvent, &itemHit))
  232.     {
  233.         switch (itemHit)
  234.             {
  235.             case kContinueButton:
  236.                 if ( gRadioButtonState != kDoNothing )
  237.                 {
  238.                     if ( gApplicationLaunched )
  239.                     {    // If application has been launched
  240.                         err = SetFrontProcess(&gApplicationPSN);
  241.                         if ( err == noErr )
  242.                             *wasHandled = true;                    
  243.                     }
  244.                     else
  245.                     {
  246.                         LaunchSAM();
  247.                         *wasHandled = true;
  248.                     }
  249.                 }
  250.                 break;
  251.                 
  252.             case kHelpButton:
  253.                 DisplayHelp();
  254.                 *wasHandled = true;
  255.                 break;
  256.                 
  257.             case kScanForVirusesButton:
  258.                 gRadioButtonState = kScanForViruses;
  259.                 SetRadioButtonState();
  260.                 *wasHandled = true;
  261.                 break;
  262.                 
  263.             case kRepairButton:
  264.                 gRadioButtonState = kRepair;
  265.                 SetRadioButtonState();
  266.                 *wasHandled = true;
  267.                 break;
  268.                 
  269.             case kDoNothingButton:
  270.                 gRadioButtonState = kDoNothing;
  271.                 SetRadioButtonState();
  272.                 *wasHandled = true;
  273.                 break;
  274.                 
  275.             default:
  276.                 break;
  277.         }
  278.     }
  279.         
  280.     return(err);
  281. }
  282.  
  283. //--------------------------------------------------------------------------------
  284. //    DisplayHelp
  285. //--------------------------------------------------------------------------------
  286.  
  287. void DisplayHelp()
  288. {
  289.     ShellErr    err = noErr;
  290.  
  291.     if (gHelpPanelOpen)                            // Is it already open ?
  292.     {
  293.         err = PSShowPanel(gHelpPanel);
  294.     }
  295.     else
  296.     {
  297.         PSDisplayHelpWindow(gHelpPanel);
  298.         gHelpPanelOpen = true;
  299.     }
  300.         
  301.     if (err != noErr)
  302.     {
  303.         PSErrorAlert( err, true, "\p", "\p", "\p", "\p", kQuitButtonIndex, kContinueNotQuitBtnIndex );
  304.         PSQuitShell(kDontAllowUserToContinue);
  305.     }
  306. }
  307.  
  308.  
  309. //--------------------------------------------------------------------------------
  310. //    SetupSAMPanel
  311. //
  312. //    Sets up the various user items in the panel to their correct type, specifiying
  313. //    the data found in the SAM preference resource which is read from the ClientData
  314. //    file.
  315. //    This routine should be called after PSSetupNewPanel() but before PSShowPanel() for
  316. //    the SAM panel.
  317. //
  318. //    Parameters
  319. //
  320. //    Returns
  321. //--------------------------------------------------------------------------------
  322.  
  323. static OSErr    SetupSAMPanel( SInt16 inPrefRsrcID )
  324. {
  325.     SAMPrefsHandle        prefsHandle;
  326.     SAMPrefsPtr            prefsPtr;
  327.     OSErr                err = noErr;
  328.     
  329.     
  330.     if( inPrefRsrcID == 0 )
  331.     {
  332.         SInt16** theIDHandle = (SInt16**) GetResource( 'paul', 128 );    // Get Resource ID for plugin preference
  333.         if (theIDHandle != NULL)
  334.             inPrefRsrcID = **theIDHandle;
  335.         else
  336.             inPrefRsrcID = 5000;
  337.     }
  338.  
  339.     
  340.     prefsHandle = (SAMPrefsHandle)GetResource(kSAMPrefsResType, inPrefRsrcID);
  341.     
  342.     if (prefsHandle != NULL)
  343.     {
  344.         if ((**prefsHandle).format == 0)
  345.         {
  346.             TEHandle                text;
  347.             short                    fontNum,
  348.                                     fontStyle,
  349.                                     fontSize;
  350.             PanelItemType            itemType;
  351.             Handle                    itemHandle;
  352.             Rect                    itemRect;
  353.             PicHandle                picHandle    = NULL;
  354.             
  355.             HLock((Handle)prefsHandle);
  356.             prefsPtr = *prefsHandle;
  357.             
  358.             gAppRefID = prefsPtr->SAMAppFileRefRsrsID;
  359.             
  360.             // Setup the panel title string.
  361.             err = PSGetPanelItem(gSAMPanel, kTitleTextItem, &itemType, &itemHandle, &itemRect);
  362.             if (err == noErr)
  363.             {
  364.                 if (PSReadFontInfo(kFontInfoInShell, kUpgraderFonts, kLargeTextStyle, &fontNum, &fontStyle, &fontSize))
  365.                 {
  366.                     text = PSNewStyledStringItem(&itemRect, prefsPtr->SAMSTRListRsrsID, 1, fontNum, fontStyle, fontSize);
  367.                     if (text != NULL)
  368.                         err = PSSetPanelItem(gSAMPanel, kTitleTextItem, kStyledStringType, (Handle)text, &itemRect);
  369.                     else
  370.                         err = kCannotLoadNeededResourceErr;
  371.                 }
  372.             }
  373.             if (err == noErr)
  374.             {    
  375.                 // Setup the SAM panel main text.
  376.                 if (prefsPtr->mainTextReferenceID != 0)                            // Ignore id 0 if the Client does not want any text
  377.                 {
  378.                     err = PSGetPanelItem(gSAMPanel, kSAMPanelTextItem, &itemType, &itemHandle, &itemRect);
  379.                     if (err == noErr) 
  380.                     {
  381.                         text = PSNewStyledTextItem(&itemRect, prefsPtr->mainTextReferenceID);
  382.                         if (text != NULL)
  383.                             err = PSSetPanelItem(gSAMPanel, kSAMPanelTextItem, kStyledTextType, (Handle)text, &itemRect);
  384.                         else
  385.                             err = kCannotLoadNeededResourceErr;
  386.                     }
  387.                 }
  388.             }
  389.             HUnlock((Handle)prefsHandle);
  390.         }
  391.         else
  392.         {
  393.             err = kUnsupportedPrefsFormatErr;
  394.         }
  395.     }
  396.     else
  397.     {
  398.         err = kNoPrefsErr;
  399.     }
  400.         
  401.     PSSetPanelItemAction(gSAMPanel, kContinueButton, kDefaultButtonMask);
  402.     PSSetPanelItemAction(gSAMPanel, kContinueButton, kContinueButtonMask);
  403.     PSSetPanelItemAction(gSAMPanel, kGoBackButton, kGoBackButtonMask);
  404.  
  405.     if (err == noErr)
  406.         err = SetupHelpPanel( inPrefRsrcID );
  407.         
  408.     return(err);
  409. }
  410.  
  411. //--------------------------------------------------------------------------------
  412. //    SetupHelpPanel
  413. //
  414. //    Sets up the various user items in the panel to their correct type, specifiying
  415. //    the data found in the SAM preference resource which is read from the ClientData
  416. //    file.
  417. //    This routine should be called after PSSetupNewPanel() but before PSShowPanel() for
  418. //    the Help panel.
  419. //
  420. //    Parameters
  421. //
  422. //    Returns
  423. //--------------------------------------------------------------------------------
  424.  
  425. static OSErr    SetupHelpPanel( SInt16 inPrefRsrcID )
  426. {
  427.     SAMPrefsHandle        prefsHandle;
  428.     SAMPrefsPtr            prefsPtr;
  429.     OSErr                err         = noErr;
  430.     
  431.     prefsHandle = (SAMPrefsHandle)GetResource(kSAMPrefsResType, inPrefRsrcID);
  432.     
  433.     if (prefsHandle != NULL)
  434.     {
  435.         if ((**prefsHandle).format == 0)
  436.         {
  437.             Str255        helpPanelTitle;
  438.             
  439.             HLock((Handle)prefsHandle);
  440.             prefsPtr = *prefsHandle;
  441.             
  442.             if ((prefsPtr->flags & kHelpTextInFile) == kHelpTextInFile)
  443.             {
  444.                 
  445.                 GetIndString(helpPanelTitle, prefsPtr->SAMSTRListRsrsID, 2);
  446.                 err = PSSetupHelpWindow(kReadFromSimpleTextFile, prefsPtr->helpTextReferenceID, 
  447.                     prefsPtr->baseHelpPICTResID, helpPanelTitle, &gHelpPanel );
  448.             }
  449.             else
  450.             {
  451.  
  452.                 GetIndString(helpPanelTitle, prefsPtr->SAMSTRListRsrsID, 2);
  453.  
  454.                 err = PSSetupHelpWindow(kReadFromResourceFile, prefsPtr->helpTextReferenceID, 
  455.                     prefsPtr->baseHelpPICTResID, helpPanelTitle, &gHelpPanel );
  456.             
  457.             }
  458.  
  459.                 
  460.             HUnlock((Handle)prefsHandle);
  461.         }
  462.         else
  463.         {
  464.             err = kUnsupportedPrefsFormatErr;
  465.         }
  466.     }
  467.     else
  468.     {
  469.         err = kNoPrefsErr;
  470.     }
  471.     
  472.     return(err);
  473. }
  474.  
  475. //--------------------------------------------------------------------------------
  476. //    SetRadioButtonState
  477. //
  478. //    Parameters
  479. //
  480. //    Returns
  481. //--------------------------------------------------------------------------------
  482. static void SetRadioButtonState( void )
  483. {
  484.     PanelItemType    itemType;
  485.     Rect            itemRect;
  486.     Handle            itemHandle;
  487.     
  488.     switch (gRadioButtonState) 
  489.     {
  490.         case kScanForViruses:
  491.             if( PSGetPanelItem( gSAMPanel, kScanForVirusesButton, &itemType, &itemHandle, &itemRect ) == noErr )
  492.                 SetControlValue( (ControlHandle)itemHandle, 1 );
  493.             if( PSGetPanelItem( gSAMPanel, kRepairButton, &itemType, &itemHandle, &itemRect ) == noErr )
  494.                 SetControlValue( (ControlHandle)itemHandle, 0 );
  495.             if( PSGetPanelItem( gSAMPanel, kDoNothingButton, &itemType, &itemHandle, &itemRect ) == noErr )
  496.                 SetControlValue( (ControlHandle)itemHandle, 0 );
  497.             break;
  498.             
  499.         case kRepair:
  500.             if( PSGetPanelItem( gSAMPanel, kScanForVirusesButton, &itemType, &itemHandle, &itemRect ) == noErr )
  501.                 SetControlValue( (ControlHandle)itemHandle, 0 );
  502.             if( PSGetPanelItem( gSAMPanel, kRepairButton, &itemType, &itemHandle, &itemRect ) == noErr )
  503.                 SetControlValue( (ControlHandle)itemHandle, 1 );
  504.             if( PSGetPanelItem( gSAMPanel, kDoNothingButton, &itemType, &itemHandle, &itemRect ) == noErr )
  505.                 SetControlValue( (ControlHandle)itemHandle, 0 );
  506.             break;
  507.             
  508.         case kDoNothing:
  509.             if( PSGetPanelItem( gSAMPanel, kScanForVirusesButton, &itemType, &itemHandle, &itemRect ) == noErr )
  510.                 SetControlValue( (ControlHandle)itemHandle, 0 );
  511.             if( PSGetPanelItem( gSAMPanel, kRepairButton, &itemType, &itemHandle, &itemRect ) == noErr )
  512.                 SetControlValue( (ControlHandle)itemHandle, 0 );
  513.             if( PSGetPanelItem( gSAMPanel, kDoNothingButton, &itemType, &itemHandle, &itemRect ) == noErr )
  514.                 SetControlValue( (ControlHandle)itemHandle, 1 );
  515.             break;
  516.             
  517.         default:
  518.             break;
  519.     }
  520. }
  521.  
  522. //--------------------------------------------------------------------------------
  523. //    LaunchSAM
  524. //
  525. //    Parameters
  526. //
  527. //    Returns
  528. //--------------------------------------------------------------------------------
  529. static void LaunchSAM( void )
  530. {
  531.     AppleEvent             scanAppleEvent, returnAppleEvent;
  532.     OSErr                theErr = noErr;
  533.     AEAddressDesc        targetAddress;
  534.     AEDescList            fileList;
  535.     short                 numberOfVolumes , i;
  536.     AliasHandle            volumeAlias;
  537.     Boolean                shouldRepair = false;
  538.     FSSpec                volFileSpec;
  539.     PanelItemType        itemType;
  540.     Rect                itemRect;
  541.     Handle                itemHandle;
  542.  
  543.         
  544.     switch ( gRadioButtonState )
  545.     {
  546.         case kScanForViruses:
  547.         case kRepair:
  548.         
  549.             numberOfVolumes = CountValidDrives();
  550.  
  551.             if ( PSLaunchFile( gAppRefID, 0, NULL, kLaunchAppInFront, &gApplicationPSN ) && numberOfVolumes > 0 )
  552.             {
  553.                 gApplicationLaunched = true;
  554.                 
  555.                 // disable radio buttons -- so that if the user clicks back into the panel from Sam they'll be inactive
  556.                 if( PSGetPanelItem( gSAMPanel, kScanForVirusesButton, &itemType, &itemHandle, &itemRect ) == noErr)
  557.                     HiliteControl( (ControlHandle)itemHandle, 255 );
  558.                 if( PSGetPanelItem( gSAMPanel, kRepairButton, &itemType, &itemHandle, &itemRect ) == noErr)
  559.                     HiliteControl( (ControlHandle)itemHandle, 255 );
  560.                 if( PSGetPanelItem( gSAMPanel, kDoNothingButton, &itemType, &itemHandle, &itemRect ) == noErr)
  561.                     HiliteControl( (ControlHandle)itemHandle, 255 );
  562.                                 
  563.                 theErr = AECreateList( NULL, 0, false, &fileList );            // create a list to add volume aliases to
  564.                 
  565.                 if( theErr == noErr )
  566.                 {
  567.                     for ( i = 1; i <= numberOfVolumes; i++ )
  568.                     {
  569.                         if( theErr == noErr )
  570.                             theErr = FSMakeFSSpec( gCurrentVolumeList[i].volRefNum, 1, (ConstStr255Param)gCurrentVolumeList[i].volName, &volFileSpec );
  571.                         
  572.                         if( theErr == noErr )
  573.                             theErr = NewAlias( NULL, &volFileSpec, &volumeAlias );            // add the alias to the list
  574.                         
  575.                         if( theErr == noErr )
  576.                             theErr = AEPutPtr( &fileList, 0, typeAlias, *volumeAlias, (**volumeAlias).aliasSize );
  577.                     }
  578.                 }
  579.                                 
  580.                 if( theErr == noErr )
  581.                     theErr = AECreateDesc(typeProcessSerialNumber, &gApplicationPSN, sizeof(ProcessSerialNumber), &targetAddress );
  582.  
  583.                 if( theErr == noErr )
  584.                     theErr = AECreateAppleEvent( 'AVEV', 'SCAN', &targetAddress, kAutoGenerateReturnID, kAnyTransactionID, &scanAppleEvent );
  585.  
  586.                 if( theErr == noErr )
  587.                     theErr = AEPutParamDesc( &scanAppleEvent, keyDirectObject, &fileList ); // add the volume list to the apple event
  588.  
  589.                 if( theErr == noErr && gRadioButtonState )
  590.                     theErr = AEPutParamPtr( &scanAppleEvent, 'REPR', typeBoolean, &shouldRepair, sizeof(Boolean) ); // add the repair parameter
  591.  
  592.                 theErr = AESend( &scanAppleEvent, &returnAppleEvent, kAENoReply, kAEHighPriority, kNoTimeOut, NULL, NULL );
  593.             }
  594.             
  595.             break;
  596.             
  597.         default:
  598.             break;
  599.     }
  600.             
  601. }
  602.  
  603.  
  604.  
  605. /*******************************************************************
  606. Function:        CountValidDrives()
  607. Purpose:        Counts the number of drives attached to the machine that
  608.                 can be installed onto and also build the list of said 
  609.                 machines.
  610. Params:            None
  611. Returns:        The number of valid drives attached to the machine, or 
  612.                 else zero (0), if there are no valid drives found
  613.  
  614. Note:            Fills gCurrentVolumeList with the current valid volumes
  615.                 for install which are attached to the machine
  616. *******************************************************************/
  617. unsigned short    CountValidDrives ( void )
  618. {
  619.     HParamBlockRec    pb;
  620.     short            validVolCount = 0;
  621.     ShellErr        err = noErr;
  622.  
  623.     pb.volumeParam.ioNamePtr = NULL;
  624.     pb.volumeParam.ioVolIndex = 1;
  625.     pb.volumeParam.ioVDRefNum = 0;
  626.     pb.ioParam.ioVRefNum = 0;
  627.  
  628.     do 
  629.     {
  630.         err = PBHGetVInfoSync( &pb );
  631.         if ( err == noErr && IsInstallableTarget( pb.volumeParam.ioVRefNum ) )
  632.             AddVolumeToList( pb.ioParam.ioVRefNum, ++validVolCount );
  633.  
  634.         pb.volumeParam.ioVolIndex++;
  635.     }while ( err == noErr );
  636.  
  637.     AddVolumeToList( NULL, validVolCount + 1 );                                // put in a NULL in the vRefNum field to signify end of list
  638.  
  639.     return validVolCount;
  640. }
  641.  
  642. /*******************************************************************
  643. Function:        AddVolumeToList()
  644. Purpose:        Adds a volume to the list of valid install volumes attached
  645.                 to the machine
  646. Params:            (i)   volume reference number of the volume to be added
  647.                 (ii)  the number of volumes in the list
  648. Returns:        None
  649. *******************************************************************/
  650. void    AddVolumeToList ( short vRefNum, short count )            // <21>
  651. {
  652.     Str255            theString = "\p";
  653.  
  654.     VRefNumToVName( vRefNum, theString );
  655.  
  656.     gCurrentVolumeList[count].volRefNum = vRefNum;
  657.     PStrSet ( gCurrentVolumeList[count].volName, theString );
  658. }
  659.  
  660.  
  661. //--------------------------------------------------------------------------------
  662. //    SendQuitEventToApplication
  663. //
  664. // Function that Quits gApplicationPSN
  665. //
  666. //--------------------------------------------------------------------------------
  667.  
  668. void SendQuitEventToApplication()
  669. {
  670.     OSErr         theErr             = noErr;
  671.     AppleEvent     theResultEvent;
  672.  
  673.  
  674.     // Create target desc.
  675.     AEDesc theTargetApp;
  676.     theErr = AECreateDesc(typeProcessSerialNumber, &gApplicationPSN, sizeof(gApplicationPSN), &theTargetApp);
  677.  
  678.     // Bring them to the front in case they put up a modal dialog on quit (i.e. Save dialog).
  679.     SetFrontProcess(&gApplicationPSN);
  680.  
  681.     // Create event
  682.     if (theErr == noErr)
  683.     {
  684.         theErr = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &theTargetApp, 0, 0, &theResultEvent);
  685.         AEDisposeDesc(&theTargetApp);
  686.     }
  687.  
  688.     // Send it
  689.     if (theErr == noErr)
  690.     {
  691.         AppleEvent theReply;
  692.         theReply.descriptorType = 'NULL';
  693.         theReply.dataHandle = NULL;
  694.  
  695.         theErr = AESend(&theResultEvent, &theReply, kAENoReply, kAENormalPriority, 200, NULL, NULL);
  696.  
  697.         AEDisposeDesc(&theReply);
  698.         AEDisposeDesc(&theResultEvent);
  699.     }
  700. }
  701.  
  702.  
  703. //--------------------------------------------------------------------------------
  704. //    SetUpAppleEventHandlers
  705. //--------------------------------------------------------------------------------
  706.  
  707. ShellErr SetUpAppleEventHandlers()
  708. {
  709.     ShellErr     err         = noErr;
  710.     gChildDiedHandlerUPP     = NewAEEventHandlerProc(ChildDiedHandler);
  711.  
  712.     // Any application
  713.     err = AEInstallEventHandler(kCoreEventClass, kAEApplicationDied, gChildDiedHandlerUPP, kAEApplicationDied, false);
  714.     if (err != noErr)
  715.         err = kInternalErr;
  716.  
  717.     return(err);
  718. }
  719.  
  720.  
  721. //--------------------------------------------------------------------------------
  722. //    RemoveAppleEventHandlers
  723. //--------------------------------------------------------------------------------
  724.  
  725. ShellErr RemoveAppleEventHandlers()
  726. {
  727.     ShellErr     err = noErr;
  728.  
  729.     err = AERemoveEventHandler(kCoreEventClass, kAEApplicationDied, gChildDiedHandlerUPP, false);
  730.     if (err != noErr)
  731.         err = kInternalErr;
  732.     else
  733.         DisposeRoutineDescriptor(gChildDiedHandlerUPP);
  734.  
  735.     return(err);
  736. }
  737.  
  738.  
  739. //--------------------------------------------------------------------------------
  740. //    ChildDiedHandler
  741. //--------------------------------------------------------------------------------
  742.  
  743. pascal OSErr ChildDiedHandler(const AppleEvent *inAppleEvent, const AppleEvent *  outReply  , SInt32   inRefcon  )
  744. {
  745.     #pragma unused (outReply,inRefcon)
  746.     SInt32                 theActualSize;
  747.     DescType             returnedType;
  748.     ProcessSerialNumber thelQuitApplicationPSN;
  749.  
  750.     EnterPlugin();
  751.  
  752.     AEGetParamPtr(inAppleEvent, keyProcessSerialNumber, typeProcessSerialNumber, &returnedType, &thelQuitApplicationPSN, sizeof(gApplicationPSN), &theActualSize);
  753.     
  754.     // Check to make sure that the application that quit is the application that was launched
  755.     if ((thelQuitApplicationPSN.highLongOfPSN == gApplicationPSN.highLongOfPSN) && (thelQuitApplicationPSN.lowLongOfPSN == gApplicationPSN.lowLongOfPSN))
  756.     {
  757.         gApplicationLaunched = false;
  758.         PSGoToNextPlugin(kUseDefaultNextModuleName);
  759.     }
  760.     
  761.     ExitPlugin();
  762.  
  763.     return noErr;
  764. }
  765.  
  766.  
  767.  
  768.  
  769. /*******************************************************************
  770. Function:        IsInstallableTarget()
  771. Purpose:        Checks to see if the volume can be installed on.  A volume
  772.                 can be installed on if it is not invisible, if it is not locked
  773.                 and if it is "hard disk like"
  774. Params:            (i)   Volume reference number
  775. Returns:        • True    :    The volume can be installed on
  776.                 • False    :    The volume cannot be installed on
  777. *******************************************************************/
  778. Boolean    IsInstallableTarget ( SInt16 inVRefNum )
  779. {
  780.     return inVRefNum != 0 && WhereVol(inVRefNum) == kHD && VolLocked(inVRefNum) == kUnlocked && !IsVolumeInvisible(inVRefNum);
  781. }
  782.  
  783. /*******************************************************************
  784. Function:        WhereVol()
  785. Purpose:        Find out what type of volume we're dealing with
  786. Params:            (i)   Volume reference number
  787. Returns:        One of { kHD, kFloppy1, kFloppy2, kFloppy3, kAppleShare, kRAMDisk, kTOPS, kAUXVolume, kCDROM, kUnknown }
  788. *******************************************************************/
  789. VolWhereTypes    WhereVol ( SInt16 vRefNum )
  790. {
  791.     Str255             diskName;
  792.     HParamBlockRec    pb;
  793.     VolWhereTypes    resultValue;
  794.  
  795.     if ( vRefNum == badVol )
  796.         resultValue = kUnknown;
  797.  
  798.     else{
  799.         pb.ioParam.ioNamePtr = ( StringPtr ) diskName;
  800.         pb.ioParam.ioVRefNum = vRefNum;
  801.         pb.volumeParam.ioVolIndex = 0;
  802.  
  803.         if ( PBHGetVInfoSync( &pb ) == noErr ){
  804.             // —— If the volume is offline then try to figure out what it is.
  805.             if ( pb.volumeParam.ioVDrvInfo == offlineRefNum )
  806.             {
  807.                 if ( pb.volumeParam.ioVDRefNum == floppy1DrvNum )
  808.                     resultValue = kFloppy1;
  809.                 else if    ( pb.volumeParam.ioVDRefNum == floppy2DrvNum )
  810.                     resultValue = kFloppy2;
  811.                 else if    ( pb.volumeParam.ioVDRefNum == floppy3DrvNum )
  812.                     resultValue = kFloppy3;
  813.                 else if    ( IsCompactDisk( pb.volumeParam.ioVDRefNum, pb.volumeParam.ioVFSID ) )    // It's a CD-ROM disk.
  814.                     resultValue = kCDROM;
  815.                 else if    ( pb.volumeParam.ioVFSID == kHFSFileManagerID )    // It's a normal HFS volume, assume it's some HD
  816.                     resultValue = kHD;
  817.                 else
  818.                     resultValue = kUnknown;
  819.             }
  820.  
  821.             else
  822.             {                                                        // —— If the volume is online then try to figure out what it is.
  823.                 if ( pb.volumeParam.ioVFSID == AppleShareFileManagerID )        // It's an AppleShare volume
  824.                     resultValue = kAppleShare;
  825.                 else if    ( pb.volumeParam.ioVFSID == TOPSFileManagerID )
  826.                         resultValue = kTOPS;
  827.                 else if    ( pb.volumeParam.ioVDRefNum == SONYRefNum && pb.volumeParam.ioVDrvInfo == floppy1DrvNum )            // It's a floppy
  828.                         resultValue = kFloppy1;
  829.                 else  if ( pb.volumeParam.ioVDRefNum == SONYRefNum && pb.volumeParam.ioVDrvInfo == floppy2DrvNum )        // It's a floppy
  830.                         resultValue = kFloppy2;
  831.                 else if    ( pb.volumeParam.ioVDRefNum == SONYRefNum && pb.volumeParam.ioVDrvInfo == floppy3DrvNum )    // It's a floppy
  832.                         resultValue = kFloppy3;
  833.                 else if    ( pb.volumeParam.ioVDRefNum == oldHDRefNum )                                        // It's an old HD 20
  834.                         resultValue = kHD;
  835.                 else if    ( IsCompactDisk(pb.volumeParam.ioVDRefNum, pb.volumeParam.ioVFSID ) )            // It's a CD-ROM disk
  836.                         resultValue = kCDROM;
  837.                 else if    ( pb.volumeParam.ioVFSID == kHFSFileManagerID )                            // It's a normal HFS volume, assume it's some HD
  838.                     resultValue = kHD;
  839.                 else
  840.                     resultValue = kUnknown;
  841.             }
  842.         }
  843.         else
  844.             resultValue = kUnknown;
  845.     }
  846.  
  847.     return resultValue;
  848. }
  849.  
  850.  
  851. /*******************************************************************
  852. Function:        IsCompactDisk()
  853. Purpose:        Check to see if the volume is a CD 
  854. Params:            (i)   Drive reference number
  855.                 (ii)  File Spec ID
  856. Returns:        • True    :    The volume is a CD
  857.                 • False    :    The volume is not a CD
  858. *******************************************************************/
  859. Boolean    IsCompactDisk ( SInt16 pDrRefNum, SInt16 pFSId )
  860. {
  861.     Boolean                    result = false;
  862.     DCtlHandle                theDctl;
  863.     StringPtr                thePtr, appleCDStr = "\p.AppleCD";
  864.     DriverGestaltParam        pb;
  865.     OSErr                    status;
  866.     short                    driverNumber, driveNumber;
  867.     long                    sysVersion;
  868.  
  869.     // First check to see if it happens to be one of the drivers we know of.
  870.     if ( ( pFSId == kHighSierraFileManagerID ) || ( pFSId == kISO9660FileManagerID ) || ( pFSId == kAudioCDFileManagerID ) )
  871.     {
  872.         result = true;
  873.     }
  874.     else
  875.     {
  876.         // If pDrRefNum is actually a drive number because the volume is ejected
  877.         // then get its driver ref num.
  878.         if ( pDrRefNum > 0 ){
  879.             driverNumber = DriveNumToDriverRefNum( pDrRefNum );
  880.             driveNumber = pDrRefNum;
  881.         }
  882.         else
  883.         {
  884.             driverNumber = pDrRefNum;
  885.             driveNumber = DriverNumberToDriveNumber( pDrRefNum );
  886.         }
  887.  
  888.         if ( Gestalt( gestaltSystemVersion, &sysVersion ) == noErr && sysVersion >= 0x0753 )
  889.         {
  890.             // use the DriverGestalt stuff introduced with PowerSurge machines
  891.             pb.csCode = kDriverGestaltCode;                    // Setup Driver Gestalt PB
  892.             pb.driverGestaltSelector = kdgDeviceType;        // ask for Device Type
  893.             pb.ioCRefNum = driverNumber;                    // Get the driver refNum
  894.             pb.ioVRefNum = driveNumber;                        // Get the drive refNum
  895.  
  896.             status = PBStatusSync( ( ParmBlkPtr ) &pb );    // Do a Driver Gestalt call
  897.  
  898.             if ( ( status == noErr ) && ( pb.driverGestaltResponse == kdgCDType ) )     
  899.                 result = true;                                // Device type is 'cdrm'
  900.         }
  901.         else
  902.         {
  903.             // use the old method
  904.             theDctl = GetDCtlEntry( pDrRefNum );
  905.             if ( theDctl != NULL )
  906.             {
  907.                 if ( ( *theDctl )->dCtlFlags & 0x0040 )
  908.                   thePtr = ( StringPtr ) *( ( Handle ) ( ( *theDctl )->dCtlDriver ) );
  909.                 else
  910.                   thePtr = ( StringPtr ) ( ( *theDctl )->dCtlDriver );
  911.  
  912.                 thePtr = ( StringPtr ) ( thePtr + 18 );
  913.  
  914.                 if ( PStrCmp( thePtr, appleCDStr ) == 0 )
  915.                     result = true;
  916.             }
  917.         }
  918.     }
  919.  
  920.     return result;
  921.  
  922. }
  923.  
  924. /*******************************************************************
  925. Function:        IsVolumeInvisible()
  926. Purpose:        Check to see if the volume is invisible
  927. Params:            (i)   Volume reference number
  928. Returns:        • True    :    The volume is invisible
  929.                 • False    :    The volume is not invisible
  930. *******************************************************************/
  931. Boolean    IsVolumeInvisible ( SInt16 pVRefNum )
  932. {
  933.     #if GENERATINGPOWERPC
  934.         #pragma options align=mac68k
  935.     #endif
  936.  
  937.     typedef struct{
  938.         Handle    localHand;             // handle to local info cache
  939.         SInt16    version;            // $0001 for first version
  940.         SInt16    altPrivModel;        // ***to be defined
  941.         SInt32    attrib;                // attribute bits for this volume
  942.         SInt32    serverAdr;             // network address of the server
  943.         SInt32     volumeSpeed;        // rating of volume speed in milliseconds
  944.  
  945.         // where do we get these?
  946.         SInt32     minimalBufferSize;    // buffer size to use which show reasonable feedback
  947.         SInt32    optimalBufferSize;    // buffer size of diminishing return
  948.     } MetaInfo;
  949.  
  950.     #if GENERATINGPOWERPC
  951.         #pragma options align=reset
  952.     #endif
  953.  
  954.     CInfoPBRec        volCatInfoPB;
  955.     HParamBlockRec    volInfoPB, volParmsPB;
  956.     OSErr            theErr, gestaltErr;
  957.     SInt32            FSTVers;
  958.     MetaInfo        meta;
  959.     Boolean            isInvisible = false;
  960.  
  961.     volCatInfoPB.hFileInfo.ioNamePtr = NULL;
  962.     volCatInfoPB.hFileInfo.ioVRefNum = pVRefNum;
  963.     volCatInfoPB.hFileInfo.ioFDirIndex = -1;
  964.     volCatInfoPB.hFileInfo.ioDirID = 0;
  965.  
  966.     theErr = PBGetCatInfoSync( &volCatInfoPB );
  967.  
  968.     if ( theErr == noErr ){
  969.         isInvisible = ( volCatInfoPB.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible ) == fInvisible;
  970.  
  971.          if ( isInvisible )
  972.          {
  973.             // check #1, HS or ISO with wold FST?
  974.             volInfoPB.volumeParam.ioNamePtr = NULL;
  975.             volInfoPB.volumeParam.ioVRefNum = pVRefNum;
  976.             volInfoPB.volumeParam.ioVolIndex = 0;
  977.             theErr = PBHGetVInfoSync( &volParmsPB );
  978.     
  979.             if ( ( theErr == noErr ) && ( ( volInfoPB.volumeParam.ioVFSID == 0x4242 ) || ( volInfoPB.volumeParam.ioVFSID == 0x4147 ) ) )
  980.             {
  981.                 gestaltErr = Gestalt( 'hscd', &FSTVers );
  982.                 if ( ( gestaltErr != noErr ) || ( FSTVers < 0x0203 ) )
  983.                 {    // if gestaltErr != noErr, then we're running an older FST that does not register Gestalt
  984.                     isInvisible = false;
  985.                 }
  986.             }
  987.  
  988.             // check #2, boot volume?
  989.             if ( gSysVolRefNum == pVRefNum )
  990.                 isInvisible = false;
  991.  
  992.             // check #3, appleshare volume?        
  993.             volParmsPB.ioParam.ioNamePtr = NULL;
  994.             volParmsPB.ioParam.ioVRefNum = pVRefNum;
  995.             volParmsPB.ioParam.ioReqCount =    sizeof(MetaInfo);
  996.             volParmsPB.ioParam.ioBuffer = ( Ptr ) &meta;
  997.             if ( ( PBHGetVolParmsSync( &volParmsPB ) == noErr ) && ( meta.serverAdr != 0 ) )
  998.                 isInvisible = false;
  999.         }
  1000.     }
  1001.  
  1002.     return isInvisible;
  1003. }
  1004.  
  1005. /*******************************************************************
  1006. Function:        VolLocked()
  1007. Purpose:        Check to see if the volume is locked
  1008. Params:            (i)   Volume reference number
  1009. Returns:        One of { Unlocked, Hardware locked, Software locked }
  1010. *******************************************************************/
  1011. LockTypes    VolLocked ( SInt16 vRefNum )
  1012. {
  1013.     HParamBlockRec    pb;
  1014.     LockTypes        value_VolLocked;
  1015.  
  1016.     pb.ioParam.ioNamePtr = NULL;
  1017.     pb.ioParam.ioVRefNum = vRefNum;
  1018.     pb.volumeParam.ioVolIndex = 0;
  1019.  
  1020.     if ( PBHGetVInfoSync( &pb ) != noErr )
  1021.         // Although there is an error here, we'll say it's unlocked because we want a better error to be determined by another routine later.
  1022.         value_VolLocked = kUnlocked;
  1023.     else if ( BTst(pb.volumeParam.ioVAtrb, 7 ) )
  1024.         value_VolLocked = kHWLock;
  1025.     else if ( BTst( pb.volumeParam.ioVAtrb, 15 ) )
  1026.         value_VolLocked = kSWLock;
  1027.     else
  1028.         value_VolLocked = kUnlocked;
  1029.  
  1030.     return value_VolLocked;
  1031. }
  1032.  
  1033. /*******************************************************************
  1034. Function:        DriveNumToDriverRefNum()
  1035. Purpose:        Given the drive number, get the driver reference number
  1036.                 from the drive queue
  1037. Params:            (i)   drive number
  1038. Returns:        Success : driver reference number
  1039.                 Failure: 0 i( no drive queue element available )
  1040. *******************************************************************/
  1041. short     DriveNumToDriverRefNum ( short driveNum )
  1042. {
  1043.      DrvQElPtr drvQElem;
  1044.  
  1045.      // find the drive queue element
  1046.      drvQElem = ( DrvQElPtr ) ( GetDrvQHdr()->qHead );
  1047.      while ( ( drvQElem != NULL ) && ( drvQElem->dQDrive != driveNum ) )
  1048.           drvQElem = ( DrvQElPtr ) ( drvQElem->qLink );
  1049.  
  1050.      if ( drvQElem != NULL )
  1051.           return drvQElem->dQRefNum;
  1052.      else
  1053.           return 0;
  1054. }
  1055.  
  1056. /*******************************************************************
  1057. Function:        DriverNumberToDriveNumber()
  1058. Purpose:        Given the driver reference number, get the associated drive number
  1059. Params:            (i)   driver reference number
  1060. Returns:        Success : drive number
  1061.                 Failure: 0 i( no drive queue element available )
  1062. *******************************************************************/
  1063. short    DriverNumberToDriveNumber ( SInt16 pDrRefNum )
  1064. {
  1065.     DrvQElPtr        drvQElem;
  1066.  
  1067.     drvQElem = (DrvQElPtr) (GetDrvQHdr()->qHead);
  1068.     
  1069.     while ((drvQElem != NULL ) && (drvQElem->dQRefNum != pDrRefNum))
  1070.         drvQElem = (DrvQElPtr) (drvQElem->qLink);
  1071.  
  1072.     if (drvQElem != NULL)
  1073.         return drvQElem->dQDrive;
  1074.     else
  1075.         return 0;
  1076.  
  1077. }
  1078.  
  1079. /*******************************************************************
  1080. Function:        VRefNumToVName()
  1081. Purpose:        Converts the volume reference number to its name 
  1082. Params:            (i)   Volume reference number
  1083.                 (ii)  Volume name (returned)
  1084. Returns:        None
  1085. *******************************************************************/
  1086. void    VRefNumToVName ( SInt16 vRefNum, Str31 volName )
  1087. {
  1088.     ParamBlockRec pb;
  1089.  
  1090.     volName[0] = 0;
  1091.     pb.ioParam.ioNamePtr = ( StringPtr ) volName;
  1092.     pb.ioParam.ioVRefNum = vRefNum;
  1093.     pb.volumeParam.ioVolIndex = -1;
  1094.  
  1095.     PBGetVInfoSync( &pb );
  1096. }
  1097.  
  1098.  
  1099. /*******************************************************************
  1100. Function:        PStrCmp()
  1101. Purpose:        compare one pascal string with another (p1 == p2 ?)
  1102. Params:            (i)   first pascal string to compare
  1103.                 (ii)  second pascal string to compare
  1104. Returns:        p1 == p2 : 0 
  1105.                 p1 > p2  : < 0
  1106.                 p1 < p2  : > 0
  1107. *******************************************************************/
  1108. short    PStrCmp ( ConstStr255Param p2, ConstStr255Param p1 )
  1109. {
  1110.     short    i, len;
  1111.     
  1112.     if ( ( unsigned char ) *p2 == ( unsigned char ) *p1 )
  1113.     {
  1114.         len = ( unsigned char ) *p2;
  1115.         for ( i=1; *p2 == *p1; p2++, p1++, i++ )
  1116.             if ( i > len )
  1117.                 return 0;
  1118.  
  1119.         return ( ( unsigned char ) *p2 - ( unsigned char ) *p1 );
  1120.     }
  1121.     else 
  1122.         return ( ( unsigned char ) *p2 - ( unsigned char ) *p1 );    
  1123. }
  1124.  
  1125.  
  1126. /*******************************************************************
  1127. Function:        PStrSet()
  1128. Purpose:        assign one pascal string to another (destination = source)
  1129. Params:            (i)   destination pascal string
  1130.                 (ii)  source pascal string
  1131. Returns:        none
  1132. *******************************************************************/
  1133. void    PStrSet ( Str255 destination, ConstStr255Param source )
  1134. {
  1135.     BlockMove( source, destination, *source + 1 );
  1136. }
  1137.